﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using CoolCode.Share;
using CoolCode.Json;

namespace CoolCode.Data.Xml
{
    public class DataBase :CoolCode.Data.IDataBase //IDisposable, 
    {

        public string Path { get; set; }

        public DataBase()
        {

        }

        public DataBase(string path)
        {
            this.Path = path.EndsWith(@"\") ? path : path + @"\";
        }

        public string GetFileName<T>()
        {
            string className = BLReflection.GetClassName<T>();
            return GetFileName(className);
        }

        public string GetFileName(string className)
        {
            return Path + className + ".xml";
        }

        #region IDataBase Members

        public bool Contains<T>(Predicate<T> predicate)
        {
            //var table = TableFactory.Instance.GetTable<T>(Path);
            //var className = table.Name;
            //var root = table.Root;
            //var fileName = table.FileName;
            //var xe = root.Descendants(className).Where (c => predicate (c));
            var q = Query<T>().Where(c => predicate(c));
            return q.Count() > 0;
        }

        public bool Contains<T>(T TEntity)
        {
            var table = TableFactory.Instance.GetTable<T>(Path);
            var className = table.Name;
            var root = table.Root;
            var fileName = table.FileName;
            var xe = root.Descendants(className).Where(c => PrimaryKeyExpression(c, TEntity));
            return xe.Count() > 0;
        }

        public void Store<T>(T TEntity)
        {
            //var className = BLReflection.GetClassName<T>();
            //var fileName = GetFileName(className);
            //var root = Load(fileName); 
            //var xe = Entity2Xml<T>(TEntity);
            //root.Add(xe);
            //root.Save(fileName);

            //Get primary key where IsPrimaryKey of ColumnAttribute is true.
            string[] primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);

            SaveByPrimaryKeys<T>(TEntity);
        }

        public void Delete<T>(T TEntity)
        {
            //var className = BLReflection.GetClassName<T>();
            //var fileName = GetFileName(className);
            //var root = Load(fileName); 
            var table = TableFactory.Instance.GetTable<T>(Path);
            var className = table.Name;
            var root = table.Root;
            var fileName = table.FileName;
            var xe = root.Descendants(className).Single(c => PrimaryKeyExpression(c, TEntity));
            xe.Remove();
            root.Save(fileName);
        }

        public void StoreAll<T>(IEnumerable<T> TList)
        {
            //var className = BLReflection.GetClassName<T>();
            //var fileName = GetFileName(className);
            //var root = Load(fileName);
            var table = TableFactory.Instance.GetTable<T>(Path);
            var className = table.Name;
            var root = table.Root;
            var fileName = table.FileName;
            foreach (T TEntity in TList)
            {
                //var dict = BLReflection.ToDictionary<T>(TEntity);

                //XElement xe = new XElement(className);
                //foreach (var d in dict)
                //    xe.Add(new XElement(d.Key, d.Value));
                var xe = XmlConvert.Entity2Xml<T>(TEntity);
                root.Add(xe);
            }
            root.Save(fileName);
        }

        public void DeleteAll<T>(IEnumerable<T> TList)
        {
            var table = TableFactory.Instance.GetTable<T>(Path);
            var className = table.Name;
            var root = table.Root;
            var fileName = table.FileName;
            var xe = root.Descendants(className);

            string[] primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
            IDictionary<string, object>[] kvList = new IDictionary<string, object>[TList.Count()];
            int i = 0;
            foreach (var TEntity in TList)
            {
                //var kv = new Dictionary<string, string>();
                //foreach (var pk in primaryKeys)
                //{
                //    kv.Add(pk, BLReflection.GetPropValue(TEntity, pk) as string);
                //}
                var kv = BLReflection.ToDictionary(TEntity);
                kvList[i++] = kv;
            }
            var xeList = xe.ToList();
            for (int k = 0; k < xeList.Count(); k++)
            {
                var x = xeList[k];
                if (x == null)
                    continue;
                bool delete = false;
                for (i = 0; i < kvList.Length; i++)
                {
                    bool found = true;
                    foreach (var pk in primaryKeys)
                    {
                        if (!IsEqual(x.Element(pk).Value, kvList[i][pk]))
                        {
                            found = false;
                            break;
                        }
                    }
                    if (found)
                    {
                        delete = true;
                        break;
                    }
                }
                if (delete)
                {
                    x.Remove();
                }
            }
            root.Save(fileName);
        }

        public IList<T> Query<T>()
        {
            //var className = BLReflection.GetClassName<T>();
            //var fileName = GetFileName(className);
            //var root = Load(fileName);
            var table = TableFactory.Instance.GetTable<T>(Path);
            var className = table.Name;
            var root = table.Root;
            var fileName = table.FileName;
            var xList = root.Descendants(className);
             
            var result = from x in xList select XmlConvert.Xml2Entity<T>(x);
            return result.ToList();
            //foreach (var x in xList)
            //{
            //    T t = Xml2Entity<T>(x);
            //    result.Add(t);
            //}
            // return result;
        }


        public IList<T> Query<T>(Predicate<T> predicate)
        {
           var query = Query<T>().Where(c => predicate(c));
            //var table = TableFactory.Instance.GetTable<T>(Path);
            //var className = table.Name;
            //var root = table.Root;
            //var fileName = table.FileName; 
            //ConditionBuilder cond =new ConditionBuilder(predicate .);
            //var result =cond.Result as System.Linq.Expressions.Expression<Func<XElement, bool>>;
            //var query = root.Descendants(className).Where (result.Compile ());
            return query.ToList();
        }

        #endregion


        public XDocument Load(string fileName)
        {
            if (!System.IO.File.Exists(fileName))
            {
                return XDocument.Parse("<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
                       "<Sheet CreateDate=\"" + System.DateTime.Now + "\">" +
                        "</Sheet>"
                   );
            }
            return XDocument.Load(fileName);
        }

        public XDocument Load<T>()
        {
            var className = BLReflection.GetClassName<T>();
            var fileName = GetFileName(className);
            var root = Load(fileName);
            return root;
        }

        //public static T Xml2Entity<T>(XElement x)
        //{
        //    T t = Activator.CreateInstance<T>();
        //    var ps = BLReflection.GetProperties(t.GetType());
        //    foreach (var p in ps)
        //    {
        //        Type type = p.PropertyType;
        //        object value = null;
        //        if (type.Name == typeof(string).Name)
        //            value = x.Element(p.Name).Value;
        //        else
        //            value = XmlConvert.DeserializeObject(x.Element(p.Name).Value, type );

        //        //value = BLReflection.Parse(type,x.Element(p.Name).Value );
        //        p.SetValue(t, value, null);
        //    }
        //    return t;
        //}

        //public static XElement Entity2Xml<T>(T TEntity)
        //{
        //    var className = BLReflection.GetClassName<T>();
        //    var dict = BLReflection.ToDictionary<T>(TEntity);
        //    XElement xe = new XElement(className);
        //    foreach (var d in dict)
        //        xe.Add(new XElement(d.Key, XmlConvert.SerializeObject(d.Value )));
        //    return xe;
        //}

        //private void SaveByPrimaryKeys<T>(T TEntity)
        //{
        //    //var className = BLReflection.GetClassName<T>();
        //    //var fileName = GetFileName(className);
        //    //var root = Load(fileName);

        //    var table = TableFactory.Instance.GetTable<T>(Path);
        //    var className = table.Name;
        //    var root = table.Root;
        //    var fileName = table.FileName;
        //    // Expression<Func <XElement ,bool >> primarykeyExp ;

        //    var xeDuplicate = root.Descendants(className).Where(c => PrimaryKeyExpression(c, TEntity));
        //    xeDuplicate.Remove();

        //    var xe = XmlConvert.Entity2Xml<T>(TEntity);
        //    root.Add(xe);
        //    root.Save(fileName);

        //}

        private void SaveByPrimaryKeys<T>(T TEntity)
        { 

            var table = TableFactory.Instance.GetTable<T>(Path);
            var className = table.Name;
            var root = table.Root;
            var fileName = table.FileName;
            
            string[] primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
          var pkValue = new string [primaryKeys.Count()];
            for (int i = 0; i < primaryKeys.Count(); i++)
            {
                pkValue[i] = XmlConvert.SerializeObject ( BLReflection.GetPropValue(TEntity, primaryKeys[i]) );
            }
             ExpressionBuilder builder =new ExpressionBuilder();
            var cond = builder.Where(primaryKeys, pkValue).Compile (); 

            //var xeDuplicate = root.Descendants(className).Where(c => PrimaryKeyExpression(c, TEntity));
            var xeDuplicate = root.Descendants(className).Where( cond);
            xeDuplicate.Remove();

            var xe = XmlConvert.Entity2Xml<T>(TEntity);
            root.Add(xe);
            root.Save(fileName);

        }


        private static bool PrimaryKeyExpression<T>(XElement x, T TEntity)
        {
            string[] primaryKeys = BLReflection.GetPropFrom<System.Data.Linq.Mapping.ColumnAttribute, T>(c => c.IsPrimaryKey);
            return PrimaryKeyExpression(x, TEntity, primaryKeys);
        }

        private static bool PrimaryKeyExpression<T>(XElement x, T TEntity, string[] primaryKeys)
        {
            bool compare;
            var pkValue = new object[primaryKeys.Count()];
            for (int i = 0; i < primaryKeys.Count(); i++)
            {
                pkValue[i] = BLReflection.GetPropValue(TEntity, primaryKeys[i]);
                compare = IsEqual(x.Element(primaryKeys[i]).Value, pkValue[i]);
                if (!compare)
                    return false;
            }
            return true;
        }

        private static bool IsEqual(string x, object obj)
        {
            Type type = obj.GetType();
            //switch (type.FullName)
            //{
            //    case "System.Guid":
            //        return obj.Equals(new Guid(x)); 
            //}
            object xValue = BLReflection.Parse(type, x);
            return obj.Equals(xValue);

        }

        //#region IDisposable Members

        //public void Dispose()
        //{
        //    throw new NotImplementedException();
        //}

        //#endregion
    }
}
